package main

import (
	"bytes"
	"fmt"
	error2 "go-web-chathouse/error"
	. "go-web-chathouse/group"
	"go-web-chathouse/model"
	"go-web-chathouse/msg"
	"io"
	"io/ioutil"
	"net"
	"strings"
)

var (
	//客户端信息,用昵称为键
	//AllClientsMap = make(map[string]net.Conn)
	AllClientsMap = make(map[string]*model.Client)
	//所有群
	AllGroupsMap map[string]*Group

	//BasePath
	BasePath = "uploads/"
)

func init() {
	AllGroupsMap = make(map[string]*Group)
	AllGroupsMap["示例群"] = NewGroup("示例群", &model.Client{Name: "系统管理员"})
}
func main() {

	//建立服务端监听
	listener, e := net.Listen("tcp", "127.0.0.1:8888")
	error2.SHandleError(e, "net.Listen")
	AllClientsMap := AllClientsMap
	defer func() {
		for _, client := range AllClientsMap {
			client.Conn.Write([]byte("all:服务器进入维护状态，大家都洗洗睡吧！"))
		}
		listener.Close()
	}()

	for {
		//循环接入所有女朋友
		Conn, e := listener.Accept()
		error2.SHandleError(e, "listener.Accept")
		clientAddr := Conn.RemoteAddr()

		//TODO:接收并保存昵称
		buffer := make([]byte, 1024)
		var clientName string
		for {
			n, err := Conn.Read(buffer)
			error2.SHandleError(err, "Conn.Read(buffer)")
			if n > 0 {
				clientName = string(buffer[:n])
				break
			}
		}
		fmt.Println(clientName + "上线了")

		//TODO:将每一个女朋友丢入map
		client := &model.Client{Conn, clientName, clientAddr.String()}
		AllClientsMap[clientName] = client

		//TODO:给已经在线的用户发送上线通知——使用昵称
		for _, client := range AllClientsMap {
			client.Conn.Write([]byte(clientName + "上线了"))
		}

		//在单独的协程中与每一个具体的女朋友聊天
		go IoWithClient(client)
	}

}

//与一个Client做IO
func IoWithClient(client *model.Client) {
	//clientAddr := Conn.RemoteAddr().String()
	buffer := make([]byte, 1024)

	for {
		n, err := client.Conn.Read(buffer)
		if err != io.EOF {
			error2.SHandleError(err, "Conn.Read")
		}

		if n > 0 {
			msgBytes := buffer[:n]
			if bytes.Index(msgBytes, []byte("upload")) == 0 {
				/*处理文件上传*/

				//拿到数据包头（文件名）
				msgStr := string(msgBytes[:100])
				fileName := strings.Split(msgStr, "#")[1]

				//拿到数据包身体（文件字节）
				fileBytes := msgBytes[100:]

				//将文件字节写入指定位置
				err := ioutil.WriteFile(BasePath+fileName, fileBytes, 0666)
				error2.SHandleError(err, "ioutil.WriteFile")
				fmt.Println("文件上传成功")
				msg.SendMsg2Client("文件上传成功", client)

			} else {
				/*处理字符消息*/
				//拿到客户端消息
				msgs := string(msgBytes)
				fmt.Printf("%s:%s\n", client.Name, msgs)

				//将客户端说的每一句话记录在【以他的名字命名的文件里】
				msg.WriteMsgToLog(msgs, client)

				strs := strings.Split(msgs, "#")
				if len(strs) > 1 {
					//要发送的目标昵称
					header := strs[0]
					body := strs[1]

					switch header {

					//世界消息
					case "all":
						msg.HandleWorldMsg(client, body, AllClientsMap)

						//建群申请
					case "group_setup":
						handleGroupSetup(body, client)

						//查看群信息
					case "group_info":
						handleGroupInfo(body, client)

						//加群申请
					case "group_join":
						group, ok := AllGroupsMap[body]
						//如果群不存在
						if !ok {
							msg.SendMsg2Client("查无此群,fuckoff", client)
							continue
						}

						//发出加群申请
						msg.SendMsg2Client(client.Name+"申请加入群"+body+",是否同意？", group.Owner)
						msg.SendMsg2Client("申请已发送，请等待群主审核", client)

						//处理群主的回复
					case "group_joinreply":
						//group_joinreply#no@zhangsan@东方艺术殿堂交流群

						//拿到回复、申请人昵称、群昵称、
						strs := strings.Split(body, "@")
						answer := strs[0]
						applicantName := strs[1]
						groupName := strs[2]

						//判断是否群昵称和申请人是否合法
						group, ok1 := AllGroupsMap[groupName]
						toWhom, ok2 := AllClientsMap[applicantName]

						//自动执行加群申请
						if ok1 && ok2 {
							NewGroupJoinReply(client, toWhom, group, answer).AutoRun()
						}

					default:
						//点对点消息
						handleP2PMsg(header, client, body)
					}

				} else {
					//内容格式错误
					//客户端主动下线
					if msgs == "exit" {
						//将当前客户端从在线用户中除名
						//向其他用户发送下线通知
						for Name, c := range AllClientsMap {
							if c == client {
								delete(AllClientsMap, Name)
							} else {
								c.Conn.Write([]byte(Name + "下线了"))
							}
						}
					} else if strings.Index(msgs, "log@") == 0 {
						//log@all
						//log@张全蛋
						filterName := strings.Split(msgs, "@")[1]
						//向客户端发送它的聊天日志
						go msg.SendLog2Client(client, filterName)
					} else {
						client.Conn.Write([]byte("已阅：" + msgs))
					}

				}
			}

		}
	}

}

/*处理点对点消息*/
func handleP2PMsg(header string, client *model.Client, body string) {
	for key, c := range AllClientsMap {
		if key == header {
			c.Conn.Write([]byte(client.Name + ":" + body))

			//在点对点消息的目标端也记录日志
			go msg.WriteMsgToLog(client.Name+":"+body, c)
			break
		}
	}
}

/*处理查看群信息*/
func handleGroupInfo(body string, client *model.Client) {
	if body == "all" {
		//查看所有群信息
		info := ""
		for _, group := range AllGroupsMap {
			info += group.String() + "\n"
		}
		msg.SendMsg2Client(info, client)
	} else {
		//查看单个群信息
		if group, ok := AllGroupsMap[body]; ok {
			msg.SendMsg2Client(group.String(), client)
		} else {
			msg.SendMsg2Client("查无此群,stupid!", client)
		}

	}
}

/*处理建群申请*/
func handleGroupSetup(body string, client *model.Client) {
	if _, ok := AllGroupsMap[body]; !ok {
		//建群
		newGroup := NewGroup(body, client)

		//将新群添加到所有群集合
		AllGroupsMap[body] = newGroup

		//通知群主建群成功
		msg.SendMsg2Client("建群成功", client)
	} else {
		//要创建的群已经存在
		msg.SendMsg2Client("要创建的群已经存在", client)
	}
}
